﻿using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using SOAFramework.Library.DAL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SOAFramework.Library;
using System.Diagnostics;
using System.Collections.Concurrent;
using Analyzor.entity;

namespace Analyzor
{
    public partial class frmMain : Form
    {
        EventHandler RunWorker;
        SimpleLogger logger = new SimpleLogger();
        BackgroundWorker worker = new BackgroundWorker();
        int count = 0;
        int shardingCount = 0;
        int processed = 0;
        int processCount = 0;
        int speed = 0;
        int speedPreOne = 0;
        int speedPreTwo = 0;
        object locker = new object();
        List<Process> processes = new List<Process>();
        bool stoped = false;
        public frmMain()
        {
            InitializeComponent();
        }


        private void btnAnalyze_Click(object sender, EventArgs e)
        {
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            worker.DoWork += Worker_DoWork;
            worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
            timer1.Tick += Timer1_Tick;
            timerSpeed.Tick += TimerSpeed_Tick;
            timer1.Start();
            timerSpeed.Start();
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {


            MessageBox.Show("数据下载完成");
            btnShardingStart.Enabled = true;
            txbNumber.Enabled = true;
        }

        private void TimerSpeed_Tick(object sender, EventArgs e)
        {
            lblSpeed.Text = $"{(speed + speedPreOne + speedPreTwo)/3}/s";
            speedPreTwo = speedPreOne;
            speedPreOne = speed;
            speed = 0;
        }

        private void Timer1_Tick(object sender, EventArgs e)
        {
            progressBar1.Maximum = shardingCount;
            progressBar1.Value = processed;
            lblNumber.Text = count.ToString();
            lblProcess.Text = processCount.ToString();
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            processed = 0;
            processCount = 0;
            if (RunWorker != null)
            {
                RunWorker.Invoke(sender, e);
            }
        }

        private void btnStock_Click(object sender, EventArgs e)
        {
            frmStock frmStock = new frmStock();
            frmStock.Show();
        }

        private void btnCode_Click(object sender, EventArgs e)
        {
            frmCode code = new frmCode();
            code.Show();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            btnShardingStart.Enabled = false;
            txbNumber.Enabled = false;
            RunWorker = new EventHandler(ShardingDownload);
            worker.RunWorkerAsync();
        }

        void SingleDownload(object sender, EventArgs e)
        {
            string commandPrefix = txbTableCommand.Text;
            int threadNumber = Convert.ToInt32(txbNumber.Text);
            List<Sharding> shardingList = DbContext.StockClient.Queryable<Sharding>().ToList();
            List<entity.Cursor> cursorList = DbContext.StockClient.Queryable<entity.Cursor>().ToList();
            Dictionary<string, entity.Cursor> cusorSet = new Dictionary<string, entity.Cursor>();
            cursorList.ForEach(t =>
            {
                cusorSet[t.Code] = t;
            });
            List<Sharding> shardings = new List<Sharding>();
            shardingList.ForEach(t =>
            {
                if (!cusorSet.ContainsKey(t.StockCode))
                {
                    shardings.Add(t);
                }
                else
                {
                    var cursor = cusorSet[t.StockCode];
                    DateTime dt = Convert.ToDateTime(cursor.Value);
                    if (DateTime.Now.AddDays(-1).Date > dt)
                    {
                        shardings.Add(t);
                    }
                }
            });
            shardingCount = shardings.Count;
            Parallel.For(1, shardings.Count + 1, new ParallelOptions { MaxDegreeOfParallelism = threadNumber }, (i, loopstate) =>
            {
                if (stoped)
                {
                    return;
                }
                var sharding = shardings[i - 1];
                Process process = new Process();
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.Arguments = commandPrefix + $" {sharding.StockCode}";
                startInfo.FileName = "python";
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = true;
                process.StartInfo = startInfo;
                process.OutputDataReceived += Process_OutputDataReceived;
                process.ErrorDataReceived += Process_ErrorDataReceived;
                process.Start();
                lock (locker)
                {
                    processCount++;
                }
                processes.Add(process);
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();
                process.Close();
                lock (locker)
                {
                    processCount--;
                    processed++;
                }
            });
        }

        public void Verify(object sender, EventArgs e)
        {
            stoped = false;
            int threadNumber = Convert.ToInt32(txbNumber.Text);
            string commandPrefix = txbVerify.Text;
            shardingCount = 43;
            Parallel.For(1, 44, new ParallelOptions { MaxDegreeOfParallelism = threadNumber }, (i, loopstate) =>
            {
                if (stoped)
                {
                    return;
                }
                Process process = new Process();
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.Arguments = commandPrefix + $" {i.ToString()}";
                startInfo.FileName = "python";
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = true;
                process.StartInfo = startInfo;
                process.OutputDataReceived += Process_OutputDataReceived;
                process.ErrorDataReceived += Process_ErrorDataReceived;
                process.Start();
                lock (locker)
                {
                    processCount++;
                    processes.Add(process);
                }
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();
                process.Close();
                lock (locker)
                {
                    processCount--;
                    processed++;
                    processes.Remove(process);
                }
            });
        }

        private void ShardingDownload(object sender, EventArgs e)
        {
            stoped = false;
            int threadNumber = Convert.ToInt32(txbNumber.Text);
            string commandPrefix = txtCommand.Text;
            processes = new List<Process>();
            shardingCount = GetMaxSharding();
            Parallel.For(1, shardingCount + 1, new ParallelOptions { MaxDegreeOfParallelism = threadNumber }, (i, loopstate) =>
            {
                if (stoped)
                {
                    return;
                }
                Process process = new Process();
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.Arguments = commandPrefix + $" {i.ToString()}";
                startInfo.FileName = "python";
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = true;
                process.StartInfo = startInfo;
                process.OutputDataReceived += Process_OutputDataReceived;
                process.ErrorDataReceived += Process_ErrorDataReceived;
                process.Start();
                lock (locker)
                {
                    processCount++;
                    processes.Add(process);
                }
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();
                process.Close();
                lock (locker)
                {
                    processCount--;
                    processed++;
                    processes.Remove(process);
                }
            });
        }

        public void Stop()
        {
            stoped = true;
            foreach (var p in processes)
            {
                if (!p.HasExited)
                {
                    p.Close();
                }
            }
            processes = new List<Process>();
        }

        private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            string str = e.Data;
            if (!string.IsNullOrEmpty(str))
            {
                logger.Error(str);
            }
        }

        private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            string str = e.Data;
            if (!string.IsNullOrEmpty(str))
            {
                //logger.Debug(str);
                if (str.StartsWith("["))
                {
                    lock(locker)
                    {
                        count++;
                        speed++;
                    }
                }
                else
                {
                    logger.Debug(str);
                }
            }
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Stop();
            btnShardingStart.Enabled = true;
            txbNumber.Enabled = true;
        }

        private void btnTableStart_Click(object sender, EventArgs e)
        {
            RunWorker = new EventHandler(SingleDownload);
            worker.RunWorkerAsync();
        }

        private void btnVeryfy_Click(object sender, EventArgs e)
        {

            RunWorker = new EventHandler(Verify);
            worker.RunWorkerAsync();
        }

        private void btnSync_Click(object sender, EventArgs e)
        {
            frmSync frmSync = new frmSync();
            frmSync.Show();
        }

        public int GetMaxSharding()
        {
            var result = DbContext.StockClient.Queryable<Sharding>().Max(t => t.ShardingTable);
            return Convert.ToInt32(result);
        }

    }

    public class Item
    {
        public string name { get; set; }
        public List<string> fileds { get; set; }
    }

    public class Unit
    {
        public string name { get; set; }
        public List<Item> items { get; set; }
    }
}
